package com.such.kitx.tax.fastin;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import com.such.kit.Callable;
import com.such.kit.Logger;
import com.such.kit.cache.CacheCommons;
import com.such.kit.easyparam.ParamMap;
import com.such.kit.file.FileCommons;
import com.such.kit.file.JarFileCommons;
import com.such.kit.file.bean.FileInfo;
import com.such.kit.file.support.FileFilter;
import com.such.kit.io.source.MemoryIOSource;
import com.such.kit.io.template.ExcelIOTemplate;
import com.such.kit.io.template.XmlIOTemplate;
import com.such.kit.io.template.support.IOTemplate;
import com.such.kit.resource.support.ReadResource;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * 基于 税控发票开票软件发票信息数据接口规范 的发票信息快速导入主服务
 * 
 * 警告：此接口需要依赖开票客户端的批量导入功能
 * </pre>
 */
@WebService
public class MainServer {

	/** TEMPLATE_BASIC_PATH：模板文件根目录 */
	public static final String TEMPLATE_ROOT_PATH = "com/such/kitx/tax/fastin/template";
	public static final String TEMPLATE_TYPE_XML = "xml";
	public static final String TEMPLATE_TYPE_EXCEL = "excel";

	private static String resourceName = "fastin-main";
	private static String templateCache = CacheCommons.buildCacheName(MainServer.class, "template");

	private String filePath;
	private String backupPath;
	private String resultPath;

	/**
	 * <pre>
	 * [参考 {@link #start(String, String, String)}]
	 * </pre>
	 */
	private MainServer(String filePath, String backupPath, String resultPath) {
		this.filePath = filePath;
		this.backupPath = backupPath;
		this.resultPath = resultPath;
	}

	/**
	 * <pre>
	 * 获取报文
	 * </pre>
	 * @param taxVendor 税控服务厂商 [参考 TaxInterface.TAX_VENDOR_*]
	 * @param version 接口版本
	 * @param commodityCodeVersion 商品编码版本
	 * @return 报文
	 * @throws Exception 
	 */
	@WebMethod(action="getReport")
	@WebResult(name="report")
	public String getReport(
			@WebParam(name="taxVendor") String taxVendor, 
			@WebParam(name="version") String version, 
			@WebParam(name="commodityCodeVersion") String commodityCodeVersion
			) throws Exception {
		String report = "";
		List<FileInfo> fileInfos = FileCommons.listFolder(this.filePath, new FileFilter() {
			@Override
			public boolean accept(FileInfo fileInfo) {
				return "xls".equals(StringCommons.toLowerCase(fileInfo.getType()));
			}
		});
		if (ValidateCommons.isNotEmpty(fileInfos)) {
			Logger.info(getClass(), StringCommons.merger("抓取到 [", fileInfos.size(), "] 个待处理文件"));
			IOTemplate excelTemplate = getIOTemplate(taxVendor, version, MainServer.TEMPLATE_TYPE_EXCEL);
			MemoryIOSource excelSource = new MemoryIOSource();
			excelSource.clearInput();
			IOTemplate xmlTemplate = getIOTemplate(taxVendor, version, MainServer.TEMPLATE_TYPE_XML);
			MemoryIOSource xmlSource = new MemoryIOSource();
			xmlSource.clearOutput();

			boolean ready = true;
			int zsl = 0;
			for (FileInfo fileInfo : fileInfos) {
				String path = fileInfo.getPath();
				String name = fileInfo.getName();
				Logger.info(getClass(), StringCommons.merger("处理文件 [", name, "].开始"));
				excelTemplate.input(excelSource, path);
				List<Map<String, Object>> buyerList = excelSource.getInputResult("Gf"); // 购方
				List<Map<String, Object>> operatorList = excelSource.getInputResult("Jb"); // 经办
				List<Map<String, Object>> commodityList = excelSource.getInputResult("Sp"); // 商品
				List<Map<String, Object>> invoiceList = excelSource.getInputResult("Fp"); // 发票

				ready = true;
				Map<Object, Map<String, Object>> buyerMap = new HashMap<Object, Map<String, Object>>();
				if (ValidateCommons.isNotEmpty(buyerList)) {
					for (Map<String, Object> buyer : buyerList) {
						buyerMap.put(buyer.get("Bh"), buyer);
					}
				} else {
					ready = false;
					Logger.error(getClass(), StringCommons.merger("未在文件 [", name, "] 中抓取到购方数据"));
				}
				Map<Object, Map<String, Object>> operatorMap = new HashMap<Object, Map<String, Object>>();
				if (ValidateCommons.isNotEmpty(operatorList)) {
					for (Map<String, Object> operator : operatorList) {
						operatorMap.put(operator.get("Bh"), operator);
					}
				} else {
					ready = false;
					Logger.error(getClass(), StringCommons.merger("未在文件 [", name, "] 中抓取到经办人数据"));
				}
				ArrayListValuedHashMap<Object, Map<String, Object>> commodityMap = new ArrayListValuedHashMap<Object, Map<String, Object>>();
				if (ValidateCommons.isNotEmpty(commodityList)) {
					for (Map<String, Object> commodity : commodityList) {
						commodityMap.put(commodity.get("Zh"), commodity);
					}
				} else {
					ready = false;
					Logger.error(getClass(), StringCommons.merger("未在文件 [", name, "] 中抓取到商品数据"));
				}
				if (ready) {
					if (ValidateCommons.isNotEmpty(invoiceList)) {
						int invoiceCount = 0;
						for (Map<String, Object> invoice : invoiceList) {
							invoiceCount++;
							ready = true;
							Map<String, Object> gf = buyerMap.get(invoice.get("Gf_Bh"));
							Map<String, Object> jb = operatorMap.get(invoice.get("Jb_Bh"));
							List<Map<String, Object>> spList = commodityMap.get(invoice.get("Sp_Zh"));
							if (ValidateCommons.isEmpty(gf)) {
								ready = false;
								Logger.error(getClass(), StringCommons.merger("未找到第 [", invoiceCount, "] 条发票数据对应的购方信息"));
							}
							if (ValidateCommons.isEmpty(jb)) {
								ready = false;
								Logger.error(getClass(), StringCommons.merger("未找到第 [", invoiceCount, "] 条发票数据对应的经办信息"));
							}
							if (ValidateCommons.isEmpty(spList)) {
								ready = false;
								Logger.error(getClass(), StringCommons.merger("未找到第 [", invoiceCount, "] 条发票数据对应的商品信息"));
							}

							if (ready) {
								zsl++;
								Map<String, Object> fp = new HashMap<String, Object>();
								fp.putAll(invoice);
								fp.putAll(gf);
								fp.putAll(jb);
								fp.put("Spbmbbh", commodityCodeVersion);
								xmlSource.setOutputResult("Fp", null, fp);

								int xh = 0;
								for (Map<String, Object> sp : spList) {
									xh++;
									sp.put("Xh", xh);
									xmlSource.setOutputResult("Sph_#[Djh]", fp, sp);
								}
							}
						}
					} else {
						Logger.info(getClass(), StringCommons.merger("未在文件 [", name, "] 中抓取到发票数据"));
					}
				}
				Logger.info(getClass(), StringCommons.merger("处理文件 [", name, "].结束"));
				Logger.info(getClass(), StringCommons.merger("备份文件 [", name, "].开始"));
				FileCommons.cutFile(path, FileCommons.appendPathNode(this.backupPath, name));
				Logger.info(getClass(), StringCommons.merger("备份文件 [", name, "].结束"));
			}
			xmlTemplate.putArgs(new ParamMap<String, Object>().putParam("Zsl", zsl));
			report = xmlTemplate.output(xmlSource);
			Logger.debug(getClass(), StringCommons.merger("生成报文：", StringCommons.NEW_LINE, report));
		}
		return report;
	}

	private IOTemplate getIOTemplate(String taxVendor, final String version, final String templateType) throws Exception {
		String key = StringCommons.merger(version, "_", templateType);
		return CacheCommons.get(templateCache, key, new Callable<String, IOTemplate>() {
			public IOTemplate call(String param) throws Exception {
				IOTemplate ioTemplate = null;
				if (MainServer.TEMPLATE_TYPE_XML.equals(templateType)) {
					String templatePath = FileCommons.appendPathNode(TEMPLATE_ROOT_PATH, version, taxVendor, "KP.xml");
					ioTemplate = new XmlIOTemplate(JarFileCommons.getResourceStream(templatePath));
				} else if (MainServer.TEMPLATE_TYPE_EXCEL.equals(templateType)) {
					String templatePath = FileCommons.appendPathNode(TEMPLATE_ROOT_PATH, version, "KP.xls");
					ioTemplate = new ExcelIOTemplate(JarFileCommons.getResourceStream(templatePath));
				}
				return ioTemplate;
			}
		});
	}

	/**
	 * <pre>
	 * 启动主服务
	 * </pre>
	 * @param address WebService 服务地址
	 * @param filePath 数据文件目录
	 * @param backupPath 数据备份目录
	 * @param resultPath 反馈结果目录
	 */
	public static void start(String address, String filePath, String backupPath, String resultPath) {
		Logger.info(MainServer.class, "FastIn 主服务启动.开始");
		Endpoint.publish(address, new MainServer(filePath, backupPath, resultPath));
		Logger.info(MainServer.class, "FastIn 主服务启动.完成");
	}

	/**
	 * <pre>
	 * 主方法
	 * 为脚本启动提供入口
	 * </pre>
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		int argLen = args.length;
		if (argLen == 0) {
			Logger.info(MainServer.class, StringCommons.merger("从 ", resourceName, ".properties 中加载配置"));
			List<String> keys = ReadResource.getKeys(resourceName);
			if (ValidateCommons.isNotEmpty(keys)) {
				argLen = keys.size();
				args = new String[argLen];
				for (int i = 0; i < argLen; i++) {
					args[i] = ReadResource.getValue(resourceName, keys.get(i));
				}
			}
		}
		if (argLen < 4) {
			throw new Exception("缺少关键参数 [WebService 服务地址, 数据文件路径, 数据备份路径, 反馈结果路径]");
		}
		String address = args[0];
		String filePath = args[1];
		String backupPath = args[2];
		String resultPath = args[3];
		MainServer.start(address, filePath, backupPath, resultPath);
	}

}
